extern crate actix;
extern crate actix_rt;
use actix::prelude::*;
use std::process::{Command, Stdio};

use crate::LOCAL_CONFIG;
use crate::config::ConfigAction;


/// Define message
#[derive(Message)]
#[rtype(result = "Result<bool, std::io::Error>")]
pub struct Ping;

/// Define message
#[derive(Message)]
#[rtype(result = "Result<bool, std::io::Error>")]
pub struct Reload;

/// Define message
#[derive(Message)]
#[rtype(result = "Result<bool, std::io::Error>")]
pub struct Start;

/// Define message
#[derive(Message)]
#[rtype(result = "Result<bool, std::io::Error>")]
pub struct Stop;

// Define actor
pub struct SuricataPlugin {
    process: std::process::Child,
    monitor_eth: String,
}

fn get_home_net() -> String {
        
    use local_ip_address::list_afinet_netifas;

    let network_interfaces = list_afinet_netifas().unwrap();

    let ips = network_interfaces.iter()
        .map(|(_name, ip)| format!("{}", ip))
        .collect::<Vec<String>>()
        .join(",");
    
    format!("[{}]", ips)
}

impl SuricataPlugin {
    
    /**
     * 
     */
    pub fn new() -> Self {
        let monitor_eth = match LOCAL_CONFIG.get("monitor-eth") {
            Some(v) => v.to_string(),
            None => "ens33".to_string(),
        };
        let process: std::process::Child = Command::new("plugins/suricata/bin/suricata")
            .env("LD_LIBRARY_PATH", "plugins/suricata/lib:$LD_LIBRARY_PATH")
            .arg("-c")
            .arg("plugins/suricata/conf/suricata.yaml")
            .arg("-s")
            .arg("data/rules/current.suricata")
            .arg("-i")
            .arg(&monitor_eth)
            .arg("--set")
            .arg(format!("vars.address-groups.HOME_NET={}", get_home_net()))
            .stdout(Stdio::piped())
            .spawn()
            .expect("Failed to start suricata process");
        SuricataPlugin {
            process,
            monitor_eth
        }
    }

    fn start_plugin(&mut self) {
        
        let process: std::process::Child = Command::new("plugins/suricata/bin/suricata")
            .env("LD_LIBRARY_PATH", "plugins/suricata/lib:$LD_LIBRARY_PATH")
            .arg("-c")
            .arg("plugins/suricata/conf/suricata.yaml")
            .arg("-s")
            .arg("data/rules/current.suricata")
            .arg("-i")
            .arg(&self.monitor_eth)
            .arg("--set")
            .arg(format!("vars.address-groups.HOME_NET={}", get_home_net()))
            .stdout(Stdio::piped())
            .spawn()
            .expect("Failed to start suricata process");
        self.process = process;
    }

    fn stop_plugin(&mut self) {
        self.process.kill().expect("command wasn't running");
    }

    pub fn get_pid(&self) -> u32 {
        self.process.id()
    }

    

    
}

// Provide Actor implementation for our actor
impl Actor for SuricataPlugin {
    type Context = Context<Self>;

    fn started(&mut self, _ctx: &mut Context<Self>) {
       println!("SuricataPlugin Actor is alive");
    }

    fn stopped(&mut self, _ctx: &mut Context<Self>) {
       println!("SuricataPlugin Actor is stopped");
    }
}

/// Define handler for `Ping` message
impl Handler<Ping> for SuricataPlugin {
    type Result = Result<bool, std::io::Error>;

    fn handle(&mut self, _msg: Ping, _ctx: &mut Context<Self>) -> Self::Result {
        println!("Ping received");

        Ok(true)
    }
}


/// Define handler for `Ping` message
impl Handler<Start> for SuricataPlugin {
    type Result = Result<bool, std::io::Error>;

    fn handle(&mut self, _msg: Start, _ctx: &mut Context<Self>) -> Self::Result {
        println!("Start received");

        self.start_plugin();

        Ok(true)
    }
}

/// Define handler for `Ping` message
impl Handler<Stop> for SuricataPlugin {
    type Result = Result<bool, std::io::Error>;

    fn handle(&mut self, _msg: Stop, _ctx: &mut Context<Self>) -> Self::Result {
        println!("Stop received");

        self.stop_plugin();

        Ok(true)
    }
}


/// Define handler for `Reload` message
impl Handler<Reload> for SuricataPlugin {
    type Result = Result<bool, std::io::Error>;

    fn handle(&mut self, _msg: Reload, _ctx: &mut Context<Self>) -> Self::Result {
        println!("Reload received");

        self.stop_plugin();
        self.start_plugin();

        Ok(true)
    }
}